package pri.hillchen.std.nio.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * @author:hillchen
 * @DESCRIPTION:
 * @Date: Created in 16:47 2018/3/6 0006.
 */
public class ClientChannel {
    private String serverHost;
    private int port;
    private SocketChannel channel;
    private Selector selector = null;
    private String nickName = "";
    private boolean registed = false;

    class WriteHandel implements Runnable{
        @Override
        public void run() {
            try{
                //在主线程中 从键盘读取数据输入到服务器端
                Scanner scan = new Scanner(System.in);
                while(scan.hasNextLine()){
                    String msg = scan.nextLine();
                    if("".equals(msg)){  //不允许发空消息
                        continue;
                    } else if(!registed) {
                        nickName = msg;
                        msg = ServerChannel.REGIST_PREFIX + nickName +ServerChannel.REGIST_SUFFIX ;
                        registed = true;
                    }
                    channel.write(ServerChannel.charset.encode(msg));//client既能写也能读，这边是写
                }
                scan.close();
            }catch(Exception e){

            }
        }
    }

    class ReaderHandel implements Runnable{
        @Override
        public void run() {
            try {
                //轮询
                while(true) {
                    int readyChannels = selector.select();
                    if(readyChannels == 0){ continue;}else{
                        Set<SelectionKey> selectedKeys = selector.selectedKeys();  //可以通过这个方法，知道可用通道的集合
                        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                        while(keyIterator.hasNext()) {
                            SelectionKey key = keyIterator.next();
                            keyIterator.remove();
                            process(key);
                        }
                    }
                }
            }
            catch (IOException io){

            }
        }

        /**
         * 消息处理
         * @param key
         * @throws IOException
         */
        private void process(SelectionKey key) throws IOException {
            if(key.isReadable()){
                //使用 NIO 读取 Channel中的数据，这个和全局变量client是一样的，因为只注册了一个SocketChannel
                //client既能写也能读，这边是读
                SocketChannel sc = (SocketChannel)key.channel();

                ByteBuffer buff = ByteBuffer.allocate(1024);
                String content = "";
                while(sc.read(buff) > 0)
                {
                    buff.flip();
                    content += ServerChannel.charset.decode(buff);
                }
                //若系统发送通知名字已经存在，则需要换个昵称
                if(ServerChannel.USER_EXIST.equals(content)) {
                    nickName = "";
                    registed = true;
                }
                System.out.println(content);
                key.interestOps(SelectionKey.OP_READ);
            }
        }
    }

    public ClientChannel(String serverHost, int port) throws IOException {
        this.serverHost = serverHost;
        this.port = port;
        this.channel = SocketChannel.open();
    }

    public boolean connect() throws IOException {
        return this.channel.connect(new InetSocketAddress(serverHost,port));
    }
    public void listen(){
        new Thread(new WriteHandel()).start();
        new Thread(new ReaderHandel()).start();
    }
    public void start() throws IOException {
        if(selector == null){
            selector = Selector.open();
        }
        if(connect()){
            channel.configureBlocking(false);
            this.channel.register(this.selector, SelectionKey.OP_READ);
            this.listen();
        }
    }

    public static void main(String[] args) throws IOException {
        new ClientChannel("localhost",8080).start();
    }
}
